#include "../lib/useHead.h"
#include "../lib/stringtable.h"
#include "../lib/functable.h"
#include "../lib/instrStream.h"
#include "../lib/scriptHead.h"
#include "../lib/labelTable.h"
#include "../lib/symTable.h"
#include "../parser/lex.h"
#include "../lib/script.h"
#include "asmCompiler.h"

string ASMCompile::unescape(const string &str)
{
    static char buf[1024*8];
    int i, index = 0;

    for (i = 0; i < str.size(); ++i) {
        if (str[i] != '\\') 
            buf[index++] = str[i];
        else {
            ++i;
            if (i == str.size()) {
                buf[index++] = '\0';
            } else {
                switch(str[i]) {
                    case '\\':
                        buf[index++] = '\\';
                        break;
                    case 'n':
                        buf[index++] = '\n';
                        break;
                    case 'b':
                        buf[index++] = '\b';
                        break;
                    case 't':
                        buf[index++] = '\t';
                        break;
                    default:
                        buf[index++] = str[i];
                        break;
                }
            }
        }
    }
    
    buf[index] = '\0';
    return string(buf);
}

int ASMCompile::compile(const char *name, const char *out)
{
	if(_lex.loadFile(name)) {
        printf("source file %s not exist!\n", name);
		return -1;
	}

	vector<string> vinstr;
	_is.instrList(vinstr);
	_lex.addInstr(vinstr);

	first_scan();
	_lex.resetLex();
	second_scan();

	writeFile(out);
	return 0;
}

#define Error() { fprintf(stderr, "%s %s %d %d, %d\n", __FILE__, __FUNCTION__, __LINE__, _lex.getCurrLine(), token); abort();}


void ASMCompile::first_scan()
{
    bool   s_IsInFunc = false;
    bool   s_setStackFound = false;
    Token token;
    int s_InstrStreamSize = 0; 
    int stackIndex;
    int funcIndex;
	int entryPoint;
    int tSize;
    int CurrFuncIndex = GLOBAL_SCOPE;
	int CurrFuncLocalDataSize = - 1;
    string CurrFuncName;
    string svName;

	while (1) {
		token = _lex.getNextToken();  
		if (token == END_OF_TOKEN_STREAM)
			break;
        
		switch(token) {
			case TOKEN_TYPE_SETSTACKSIZE:
				if (s_IsInFunc)
					Error();
				if (s_setStackFound)
					Error();

				token = _lex.getNextToken();
				if (token != TOKEN_TYPE_INT)
					Error();

				_sh._sh._stackSize = atoi(_lex.getCurrLexme().c_str());
				s_setStackFound = true;
				break;

			case TOKEN_TYPE_FUNC:

				if (s_IsInFunc)
					Error();

				if (_lex.getNextToken() != TOKEN_TYPE_IDENT)
					Error();

				entryPoint = s_InstrStreamSize;
				funcIndex = _ft.add(_lex.getCurrLexme(), entryPoint);
				if (funcIndex == -1)
					Error();

				if (_lex.getCurrLexme() == MAIN_FUNC_NAME)
				{
					_sh._sh._mainIndex = funcIndex;
				}

				s_IsInFunc = true;
				CurrFuncName = _lex.getCurrLexme();
				CurrFuncIndex = funcIndex;
				CurrFuncLocalDataSize = 0;

				while ((token = _lex.getNextToken()) == TOKEN_TYPE_NEWLINE);
				if (token != TOKEN_TYPE_LBRACE)
					Error();

				break;

			case TOKEN_TYPE_RBRACE:
				if (!s_IsInFunc)
					Error();
				_ft.set(CurrFuncName, 0, CurrFuncLocalDataSize);
                CurrFuncName = "";
                CurrFuncIndex = GLOBAL_SCOPE;
				s_IsInFunc = 0;
                ++s_InstrStreamSize;
				break;

			case TOKEN_TYPE_VAR:
				if ((token = _lex.getNextToken()) != TOKEN_TYPE_IDENT)
					Error();
				
				tSize = 1;
				svName = _lex.getCurrLexme();
				token = _lex.getNextToken();
				if (token == TOKEN_TYPE_LBRACKET) {
					token = _lex.getNextToken();
					if (token != TOKEN_TYPE_INT) 
						Error();
                   
					tSize = atoi(_lex.getCurrLexme().c_str());
					
					token = _lex.getNextToken();
					if (token != TOKEN_TYPE_RBRACKET)
						Error();
				} else {
					_lex.saveToken(token);
				}

				if (s_IsInFunc)
					stackIndex = -(CurrFuncLocalDataSize + 2);
				else
					stackIndex = _sh._sh._globalDataSize;
				if (_syt.add(svName, tSize, stackIndex, CurrFuncIndex) == -1)
					Error();
				
				if (s_IsInFunc)
					CurrFuncLocalDataSize += tSize;
				else
					_sh._sh._globalDataSize += tSize;
				break;

			case TOKEN_TYPE_INSTR:
				s_InstrStreamSize++;
				_lex.skipLine();
				break;

            case TOKEN_TYPE_PARAM:
                _lex.skipLine();
                break;

			// Label
			case TOKEN_TYPE_IDENT:
                svName = _lex.getCurrLexme();
				token = _lex.getNextToken();
				if (token != TOKEN_TYPE_COLON)
					Error();

				if (!s_IsInFunc)
					Error();

				if (_lt.add(svName, s_InstrStreamSize, CurrFuncIndex) == -1)
					Error();
				break;

			default:
				break;
		}

	}

    if (s_IsInFunc)
		Error();
}




void ASMCompile::second_scan()
{
    bool   s_IsInFunc = false;
    int instrIndex;
    int stackIndex;
    int CurrFuncIndex = GLOBAL_SCOPE;
    int CurrFuncParamCount = -1;
    Instr instr;
    string fName;
    string svName, tName;

    Token token;
	while (1) {
		token = _lex.getNextToken();  
		if (token == END_OF_TOKEN_STREAM)
			break;

		switch(token) {
			case TOKEN_TYPE_FUNC:
                // get Func Name Ident;
                _lex.getNextToken();
				s_IsInFunc = true;
                fName = _lex.getCurrLexme();
				CurrFuncIndex = _ft.find(fName);
				CurrFuncParamCount = 0;
				break;

			case TOKEN_TYPE_RBRACE:
                CurrFuncIndex = GLOBAL_SCOPE;
				s_IsInFunc = false;
				
                if (fName == MAIN) {
                    instr._opCode = _is.nameCode("Exit");
				    instr._opCount = 0;
                } else {
                    instr._opCode = _is.nameCode("Ret");
				    instr._opCount = 0;
                }
                _is.addInstruction(instr);
				break;

			case TOKEN_TYPE_IDENT:
			case TOKEN_TYPE_VAR:
			case TOKEN_TYPE_SETSTACKSIZE:
				break;

			case TOKEN_TYPE_PARAM:
				token = _lex.getNextToken();
				if (token != TOKEN_TYPE_IDENT)
					Error();

				stackIndex = -(_ft.getlocalDataSize(CurrFuncIndex) + 2 + CurrFuncParamCount + 1);
				if (_syt.add(_lex.getCurrLexme(), 1, stackIndex, CurrFuncIndex) == -1)
					Error();
				++CurrFuncParamCount;
				break;

			case TOKEN_TYPE_INSTR:
				tName = _lex.getCurrLexme();
                instr._opCode = _is.nameCode(tName);
				instr._opCount = _is.getOpCount(tName);
		          
				for (COUNT i = 0; i < instr._opCount; ++i) {
					token = _lex.getNextToken();
                    OpType tempType = _is.getOpType(tName, i);
                

					if (token == TOKEN_TYPE_INT) {
						if (!(tempType & OP_FLAG_TYPE_INT))
                            Error();
                            instr._valList[i]._type = OP_FLAG_TYPE_INT;
                            instr._valList[i]._vint = atoi(_lex.getCurrLexme().c_str());

					} else if (token == TOKEN_TYPE_FLOAT) {
                        if (!(tempType & OP_FLAG_TYPE_FLOAT))
                            Error();
                            instr._valList[i]._type = OP_FLAG_TYPE_FLOAT;
                            instr._valList[i]._vfloat = atoi(_lex.getCurrLexme().c_str());

                    } else if (token == TOKEN_TYPE_STRING) {
                        int addString;
                        if (!(tempType & OP_FLAG_TYPE_STRING))
                            Error();

                            instr._valList[i]._svalue = unescape(_lex.getCurrLexme());

                            addString = _st.add(instr._valList[i]._svalue);
                            if (addString == -1)
                                Error();
                            
                            instr._valList[i]._type = OP_FLAG_TYPE_STRING;
                            instr._valList[i]._vindex = addString;

                    } else if (token == TOKEN_TYPE_REG_RETVAL) {
                        if (!(tempType & OP_FLAG_TYPE_REG))
                            Error();
                            instr._valList[i]._type = OP_FLAG_TYPE_REG;

                    } else if (token == TOKEN_TYPE_IDENT) {
                        if (tempType & OP_FLAG_TYPE_FUNC_INDEX) {
                            instr._valList[i]._svalue = _lex.getCurrLexme();
                            instr._valList[i]._vindex = _ft.find(instr._valList[i]._svalue);
                            if (instr._valList[i]._vindex == -1) {
                                if ((instr._valList[i]._vindex = _hst.add(instr._valList[i]._svalue)) == -1)
                                    Error();
                              
                                instr._valList[i]._type = OP_FLAG_TYPE_HOST_API_CALL;
                            } else 
                                instr._valList[i]._type = OP_FLAG_TYPE_FUNC_INDEX;

                        } else if (tempType & OP_FLAG_TYPE_INSTR_INDEX) {
                            instr._valList[i]._type = OP_FLAG_TYPE_INSTR_INDEX;
                            instrIndex = _lt.get(_lex.getCurrLexme(), CurrFuncIndex);
                            if (instrIndex == -1)
                                Error();
                            instr._valList[i]._vindex = instrIndex;
                            //token = GetNextToken(); // :
                            //printf("Get Label %s and token %d\n", g_tempName.c_str(), token);
                        } else if (tempType & OP_FLAG_TYPE_MEM_REF) {
                            svName = _lex.getCurrLexme();

                            if (svName == "_RetVal") {
                                instr._valList[i]._type = OP_FLAG_TYPE_REG;
                            } else {
                                token = _lex.getNextToken();

                                if (token == TOKEN_TYPE_LBRACKET){
                                    token = _lex.getNextToken();
                                    stackIndex = _syt.find(svName, CurrFuncIndex);
                                    if(stackIndex == -1)
                                        stackIndex = _syt.find(svName, GLOBAL_SCOPE);
                                    if(stackIndex == -1)
                                        Error();
                                    
                                    instr._valList[i]._vindex = _syt.get(stackIndex, SymTable::STACKINDEX);
    
                                    if (token == TOKEN_TYPE_INT) {
                                        instr._valList[i]._type = OP_FLAG_TYPE_ABS_STACK_INDEX;                                 
                                        instr._valList[i]._vindex += atoi(_lex.getCurrLexme().c_str());
                                    } else if (token == TOKEN_TYPE_IDENT) {
                                        instr._valList[i]._type = OP_FLAG_TYPE_REL_STACK_INDEX;
                                        svName = _lex.getCurrLexme();
                                        stackIndex = _syt.find(svName, CurrFuncIndex);
                                        if(stackIndex == -1)
                                            stackIndex = _syt.find(svName, GLOBAL_SCOPE);
                                        if(stackIndex == -1)
                                            Error();
                                        instr._valList[i]._offset = _syt.get(stackIndex, SymTable::STACKINDEX);
                                    } else {
                                        Error();
                                    }
                                    token = _lex.getNextToken();
                                    if (token != TOKEN_TYPE_RBRACKET)
                                        Error();
                                } else {
                                    _lex.saveToken(token);
                                    instr._valList[i]._type = OP_FLAG_TYPE_ABS_STACK_INDEX;
                                    stackIndex = _syt.find(svName, CurrFuncIndex);
                                    if(stackIndex == -1)
                                            stackIndex = _syt.find(svName, GLOBAL_SCOPE);
                                    if(stackIndex == -1) {
                                            Error();
                                    }
    
                                    instr._valList[i]._vindex = _syt.get(stackIndex, SymTable::STACKINDEX);
                                }
                            }
                        } else {
                            Error();
                        }
                    } else {
                        Error();
                    }
                        
                    token = _lex.getNextToken();
                    if (token != TOKEN_TYPE_COMMA && token != TOKEN_TYPE_NEWLINE)
                        Error(); 
                   
                } 
                
                _is.addInstruction(instr);
				
				break;

			default:
				break;
		}

	}
}
